#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <assert.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/dir.h>
#include <sys/stat.h>

#include "bstrlib.h"
#include "server.h"
#include "common.h"
#include "receive.h"
#include "send_file_listing.h"
#include "send_metadata.h"
#include "send_slice.h"

int setup_connection(void){
    /*Funcion utilizada para crear el socket y dejarlo escuchando en
    el puerto*/
    int fd_server = 0, yes=1;
    struct sockaddr_in server;

    /*Creamos el socket con protocolo de internet IPv4, de tipo stream
    (confiable)*/
    fd_server = socket(PF_INET, SOCK_STREAM, 0);
    if (fd_server == -1){
        perror("socket()");
        exit(1);
    }

    /*seteamos los valores de puerto y direccion del server utilizando
    funciones para convertir los ordenes de bytes, aumentando la portabilidad
    a otros sistemas*/

    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);
    /*INADDR_ANY se usa para autodetectar la direccion del servidor*/
    server.sin_addr.s_addr = htonl(INADDR_ANY); 

    /*Seteamos el socket para permitir que el puerto sea reusado*/
    if (setsockopt(fd_server, SOL_SOCKET, SO_REUSEADDR,\
        &yes, sizeof(int)) == -1){
        perror("setsockopt");
        exit(1);
    }

    /*Bindeamos el socket con el puerto y direccion en el servidor*/
    if(bind(fd_server, (struct sockaddr*)&server,\
       sizeof(struct sockaddr)) == -1){
        perror("bind()");
        exit(1);
    }

    return fd_server;
}

int listen_and_accept_connection(int fd_server){
    int aux_size = 0, fd_client = 0;
    char *aux_inet = NULL;
    struct sockaddr_in client;

    /*Empezamos a escuchar conexiones entrantes, vamos a atender un maximo de
    Q_CONEX conexiones*/
    if(listen(fd_server, Q_CONEX) == -1) {
        perror("listen()");
        exit(1);
    }

    printf("Esperando Conexión...\n");

    assert(fd_server != 0);
    aux_size = sizeof(struct sockaddr);
    /*Aceptamos la conexion entrante*/
    fd_client = accept(fd_server, (struct sockaddr *)&client,\
                (socklen_t *)&aux_size);
    if (fd_client < 0) {
        perror("accept()");
        exit(1);
    }
    aux_inet = inet_ntoa(client.sin_addr);
    printf("Se obtuvo una conexion desde %s\n", aux_inet);
    return fd_client;

}

int main(void){
    int fd_server = 0, fd_client = 0;

    /*Seteamos lo necesario para quedarnos escuchando en el puerto*/
    fd_server = setup_connection();

    /*Bucle que mantenemos infinitamente para aceptar todas las conexiones
    entrantes*/
    while(1){
        fd_client = listen_and_accept_connection(fd_server);
        receive_info(fd_client);
    }

    close(fd_server);
    exit(1);
}
